/**
 * 文件解析接口测试用例
 *
 * Created by snowingsea on 2020/03/05.
 */
const chai = require('chai');
const should = chai.should(); // eslint-disable-line no-unused-vars
chai.use(require('chai-http'));
const deepcopy = require('deepcopy');
const path = require('path');

const common = require('./../../common');
const Administrator = require('./../../../models/administrator');
const uri = require('./../uri');
const benchmarks = [
  require('./../../../benchmark/analyse-files/logic_bombs'),
  require('./../../../benchmark/analyse-files/self_designed'),
];

const files = require('./../../../../phoenix/models/files');
const tasks = require('./../../../../phoenix/models/tasks');

module.exports = describe('/POST project-versions/:id/files.json', function () {
  const administrator = new Administrator();
  let project = {};
  const newUserIds = [];
  function url(versionId) {
    return `/project-versions/${versionId}/files.json`;
  }

  before(function (done) {
    administrator.login()
      .then(() => administrator.createRandomTester())
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((res) => {
        project = res;
        done();
      })
      .catch(done);
  });
  after(function (done) {
    administrator.removeUsers(newUserIds)
      .then(() => {
        done();
      })
      .catch(done);
  });

  common.should.be.user(uri, url, 'post');
  common.should.be.tester(uri, url, 'post');

  // 正常发起请求
  benchmarks.forEach((benchmark) => {
    const extensions = benchmark.config.languages.c.suffixes
      .concat(benchmark.config.headers.suffixes);
    [
      {},
      { extensions },
    ].forEach((query) => {
      it(`it should analyse all files of ${benchmark.name} with ${JSON.stringify(query)}`, function (done) {
        let projectVersion;
        project.createRandomVersion()
          .then((res) => {
            projectVersion = res;
            return projectVersion.updateVersionFiles(benchmark.files, benchmark.config);
          })
          .then(() => project.creator.login())
          .then(() => {
            chai.request(uri)
              .post(url(projectVersion.versionId))
              .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
              .query(query)
              .end((err, res) => {
                res.should.have.status(202);
                common.should.be.taskMessage(res.body);
                res.body.type.should.be.eql(tasks.type.parse);
                res.body.status.should.be.eql(tasks.statusKind.waiting);
                res.body.progress.should.be.eql(0);

                project.creator.waiting4TasksOfVersionFinished(projectVersion.versionId)
                  .then(() => projectVersion.rootDirectory.getTree(extensions))
                  .then((tree) => {
                    common.should.be.fileTreeMessage(tree);
                    tree.total.should.be.eql(benchmark.result.total);
                    common.should.be.equal.tree(tree.tree, benchmark.result.tree);

                    return projectVersion.rootDirectory.getTree();
                  })
                  .then((tree) => {
                    const rootArray = [tree.tree];
                    while (rootArray.length > 0) {
                      const root = rootArray.pop();
                      if (root.children) {
                        root.children.forEach((child) => {
                          rootArray.push(child);
                        });
                      }
                      if (root.kind === files.kind.file) {
                        const extname = path.extname(root.path);
                        if (query.extensions && query.extensions.indexOf(extname) < 0) {
                          root.status.should.be.eql(files.statusKind.notCompiled);
                        }
                      }
                    }
                    done();
                  })
                  .catch(done);
              });
          })
          .catch(done);
      });
    });
  });

  // 编译部分文件
  [
    files.statusKind.compileFailed,
    files.statusKind.compiled,
  ].forEach((status) => {
    benchmarks.forEach((benchmark) => {
      it(`it should analyse files part of ${benchmark.name} with ${status}`, function (done) {
        const extensions = benchmark.config.languages.c.suffixes
          .concat(benchmark.config.headers.suffixes);
        const fileIds = [];
        const expectedTree = deepcopy(benchmark.result.tree);
        const rootArray = expectedTree.children.filter((child) => child.status !== status);
        while (rootArray.length > 0) {
          const root = rootArray.pop();
          if (root.children) {
            root.children.forEach((child) => {
              rootArray.push(child);
            });
          }
          root.status = files.statusKind.notCompiled;
        }
        if (status === files.statusKind.compileFailed) {
          expectedTree.status = files.statusKind.compileFailed;
        } else {
          expectedTree.status = files.statusKind.notCompiled;
        }
        let projectVersion;
        project.createRandomVersion()
          .then((res) => {
            projectVersion = res;
            return projectVersion.updateVersionFiles(benchmark.files, benchmark.config);
          })
          .then(() => projectVersion.rootDirectory.getTree(extensions))
          .then(({ tree }) => { // 筛选文件id
            const childrenMap = new Map();
            tree.children.forEach((child) => {
              childrenMap.set(child.fileName, child);
            });
            benchmark.result.tree.children.forEach((child) => {
              if (child.status === status) {
                fileIds.push(childrenMap.get(child.fileName).fileId);
              }
            });
            fileIds.length.should.be.above(0);
            return project.creator.login();
          })
          .then(() => {
            chai.request(uri)
              .post(url(projectVersion.versionId))
              .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
              .query({ fileIds })
              .end((err, res) => {
                res.should.have.status(202);

                project.creator.waiting4TasksOfVersionFinished(projectVersion.versionId)
                  .then(() => projectVersion.rootDirectory.getTree(extensions))
                  .then((tree) => {
                    common.should.be.fileTreeMessage(tree);
                    tree.total.should.be.eql(benchmark.result.total);
                    common.should.be.equal.tree(tree.tree, expectedTree);
                    done();
                  })
                  .catch(done);
              });
          })
          .catch(done);
      });
    });
  });

  // 没有文件需要解析
  benchmarks.forEach((benchmark) => {
    it(`it should fail when no file to analyse of ${benchmark.name}`, function (done) {
      let projectVersion;
      project.createRandomVersion()
        .then((res) => {
          projectVersion = res;
          return projectVersion.updateVersionFiles(benchmark.files, benchmark.config);
        })
        .then(() => project.creator.login())
        .then(() => {
          chai.request(uri)
            .post(url(projectVersion.versionId))
            .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
            .query({ exceptFileIds: [projectVersion.rootDirectory.fileId] })
            .end((err, res) => {
              res.should.have.status(430);
              done();
            });
        })
        .catch(done);
    });
  });

  // 不可解析冻结项目的文件
  it('it should fail to analyse file in frozen project', function (done) {
    let newProjectId;
    let newVersionId;
    project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then(({ versionId }) => {
        newVersionId = versionId;
        return project.creator.freezeProject(newProjectId);
      })
      .then(() => project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newVersionId))
          .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可解析冻结项目版本中的文件
  it('it should fail to analyse file in frozen project version', function (done) {
    let newVersionId;
    project.createRandomVersion()
      .then(({ versionId }) => {
        newVersionId = versionId;
        return project.freezeVersion(versionId);
      })
      .then(() => project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newVersionId))
          .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
          .end((err, res) => {
            res.should.have.status(403);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可解析他人项目文件
  it('it should fail to analyse file when the project is not yours', function (done) {
    let otherVersionId;
    administrator.createRandomTester()
      .then((tester) => {
        newUserIds.push(tester.userId);
        return tester.createRandomProject();
      })
      .then((project) => project.createRandomVersion())
      .then(({ versionId }) => {
        otherVersionId = versionId;
        return project.creator.login();
      })
      .then(() => {
        chai.request(uri)
          .post(url(otherVersionId))
          .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });

  // 不可解析不存在的项目文件
  it('it should fail to analyse file when the project is not exist', function (done) {
    let newProjectId;
    let newVersionId;
    project.creator.createRandomProject()
      .then((project) => {
        newProjectId = project.projectId;
        return project.createRandomVersion();
      })
      .then(({ versionId }) => {
        newVersionId = versionId;
        return project.creator.removeProject(newProjectId);
      })
      .then(() => project.creator.login())
      .then(() => {
        chai.request(uri)
          .post(url(newVersionId))
          .set('Authorization', `Bearer ${project.creator.token.accessToken}`)
          .end((err, res) => {
            res.should.have.status(404);
            common.should.be.errorMessage(res.body);
            done();
          });
      })
      .catch(done);
  });
});
